/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.ufpr.inf.escel.brain.parsers;

import br.ufpr.inf.escel.brain.model.DadoTeste;
import br.ufpr.inf.escel.brain.model.po.DadoTestePO;
import br.ufpr.inf.escel.brain.model.po.Metodo;
import br.ufpr.inf.escel.brain.model.po.Metodos;
import br.ufpr.inf.escel.brain.model.po.Parametro;
import br.ufpr.inf.escel.brain.model.po.Parametros;
import br.ufpr.inf.escel.brain.model.po.Referencias;
import br.ufpr.inf.escel.brain.util.PrimitivosUtils;
import br.ufpr.inf.escel.consts.Constantes;
import br.ufpr.inf.escel.manager.util.Arquivo;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author gustavo
 */
public class FormatarDadoTestePO extends FormatarDadoTeste {

    private static List<String> vetorMetodos;

    public FormatarDadoTestePO() {
        vetorMetodos = new ArrayList<String>();
        init(Constantes.ARQUIVO_METODOS);
    }

    /**
     * Metodo responsável por ler o arquivo de instrumentação gerado
     * pela JaBUTi para a classe em teste. Tal arquivo tem informações
     * sobre a classe e os métodos existentes nela. Essas informações
     * vão ser utilizada posteriormente para a criação do dado de teste
     * 
     * @param arquivo instrumentado
     * @param arquivo de saida, que será codificado com os metodos da cut
     * @since 0.5
     */
    private void init(String arquivoTxt) {
        if (vetorMetodos.size() == 0) {
            String[] metodosClasse = Arquivo.leia(arquivoTxt).replace("\n", "").replace("[", "").replace("]", "").split(",");

            for (int i = 0; i < metodosClasse.length; i++) {
                vetorMetodos.add(metodosClasse[i]);
            }
        }
    }

    /**
     * Recupera os metodos da cut
     *
     * @return List com os metodos da cut
     * @since 2.8
     */
    public static List getVetorMetodos() {
        return vetorMetodos;
    }

    /**
     * Metodo responsavel pela codificação do cromossomo de acordo com
     * a gramática definida por Tonella. 
     * 
     * @return uma string com a formatação do cromossomo
     * @since 0.5
     *
     * Metodo foi reescrito em: 3.8
     */
    @Override
    protected DadoTestePO formarDadoTeste() {
        Metodos methods = Metodos.getInstance();
        Parametros params = Parametros.getInstance();

        int tamanhoDT = Constantes.TAMANHO_DEFAULT;

        for (int i = 0; i < tamanhoDT; i++) {
            //sorteia um metodo
            int indexMetodo = (int) (Math.random() * vetorMetodos.size());

            if (indexMetodo == 0) {
                indexMetodo++;
            }

            Metodo metodo = methods.adicionarMetodo(vetorMetodos.get(indexMetodo));

            //verifica parametros
            if (metodo.hasParametro()) {

                Referencias refs = new Referencias(metodo.extrairParametros());
                //vetor com os parametros
                String[] novosParametros = new String[metodo.quantidadeParametros()];

                //para cada parametro
                for (int j = 0; j < novosParametros.length; j++) {
                    Parametro parametro = refs.gerarBlocoPorTipo(refs.getRef(j));
                    //parametro pode ser nulo: duvidas, consulte o metodo gerarBolocoPorTipo
                    if (parametro != null) {
                        novosParametros[j] = parametro.toString();
                    } else {
                        j--;
                    }
                }
                params.adicionarParametros(novosParametros);
            }
        }

        return new DadoTestePO(methods, params);
    }

    /**
     * Metodo que realiza a decodificação de um cromossomo formado
     * em dado de teste para OO. Recebe como entrada uma string referente 
     * ao dado de teste, e como saida, um metodo criado no formato JUnit
     * 
     * @param cromossomo completo
     * @return 
     * @since 0.7
     */
    @Override
    public String desformatarDadoTeste(DadoTeste dadoTestePO) {
        DadoTestePO novoDado = (DadoTestePO) dadoTestePO;
        String dadoTeste = novoDado.toString();

        //cria o primeiro escopo da classe
        StringBuffer classe = new StringBuffer(gerarImports(novoDado));

        //cria o metodo init()
        classe.append(variavelDeInstancia("x"));

        int metade = dadoTeste.indexOf("@");

        String primeiraMetade = dadoTeste.substring(0, metade).trim();
        String segundaMetade = dadoTeste.substring(metade + 1).trim();

        Metodos metodos = new Metodos(primeiraMetade.split(" : "));
        Parametros parametros = new Parametros(segundaMetade.split(", "));

        //concatena o corpo do metodo
        classe.append(metodo());
        int acumulado = 0;

        for (int i = 0; i < metodos.quantidade(); i++) {
            Metodo metodo = metodos.getMetodo(i);
            String m = metodo.toString();
            m = m.substring(0, m.indexOf("(") + 1).replace("$", "");
            classe.append(m);

            if (metodo.hasParametro()) {
                String[] novosParametros = new String[metodo.quantidadeParametros()];

                for (int j = 0; j < novosParametros.length; j++) {
                    novosParametros[j] = parametros.getParametro(acumulado).toString();
                    acumulado = acumulado + 1;
                }
                classe.append(PrimitivosUtils.arrayToString(novosParametros, ", "));
            }
            classe.append(");");

            classe.append("\n");
        }

        classe.append("}\n}");

        return classe.toString();
    }

    /**
     * Metodo que prepara uma string correspondente ao metodo init,
     * responsavel por instanciar a classe que vai ser testeada.
     * Recebe como parametro um caracter que vai sevir como variavel
     * para o construtor.
     * 
     * @param o nome da variavel para o construtor
     * @return corpo do metodo init
     * @since 1.0
     */
    private String variavelDeInstancia(String variavelClasse) {
        StringBuffer init = new StringBuffer();
        String nomeClasse = vetorMetodos.get(0);

        init.append(nomeClasse).append(" ").append(variavelClasse).append(";\n\n");


        init.append("@Before\n").append("public void init(){\n");


        init.append("\t");
        init.append(variavelClasse);
        init.append(" = new ");
        init.append(nomeClasse);
        init.append("();\n");
        init.append("}\n\n");

        return init.toString();
    }

    /**
     * Metodo que cria as primeiras linhas responsaveis
     * por uma classe de teste e seus imports
     * 
     * @return corpo de um escopo para classe
     * @since 1.0
     */
    private String gerarImports(DadoTestePO dado) {

        StringBuffer classe = new StringBuffer("import org.junit.Before;\n");
        classe.append("import org.junit.Test;\n\n");
        classe.append("public class ").append(dado.nomeJUnit()).append("{ \n\n");

        return classe.toString();
    }

    /**
     * Metodo que cria a assinatura do metodo que deverá 
     * testar a classe. A string com o metodo será completada
     * no metodo FormatarDadoTestePO.desformatarDadoTeste();
     * 
     * @return incio do corpo do metodo de teste
     */
    private String metodo() {
        StringBuffer metodo = new StringBuffer();

        metodo.append("@Test\n").append("public void testCase(){\n");
        return metodo.toString();
    }
}
